/*******************************************************************************
 * Copyright (c) 2003, 2004 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Common Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/cpl-v10.html
 * 
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
module dwt.browser.browser;


private import dwt.dwt;


private import dwt.browser.browseradapters;;
private import dwt.browser.browserevents;
private import dwt.browser.browserlisteners;
private import dwt.browser.website;
private import dwt.graphics.point;
private import dwt.internal.ole.win32.com;
private import dwt.internal.win32.os;
private import dwt.ole.win32.ole;
private import dwt.ole.win32.oleautomation;
private import dwt.ole.win32.olecontrolsite;
private import dwt.ole.win32.oleframe;
private import dwt.ole.win32.olemisc;
private import dwt.ole.win32.variant;
private import dwt.util.util;
private import dwt.widgets.composite;
private import dwt.widgets.event;
private import dwt.widgets.listener;
private import dwt.widgets.widget;
private import dwt.util.util;
private import dwt.util.vector;

private import dwt.internal.ole.win32.OAIDL;
private import dwt.internal.ole.win32.OBJIDL;
private import dwt.internal.ole.win32.OLEIDL;
private import dwt.internal.ole.win32.DOCOBJ;
private import dwt.internal.ole.win32.extras;
private import dwt.internal.ole.win32.MSHTMHST;
private import dwt.internal.ole.win32.ifs;


//IID IID_IWebBrowser = { 0x8856F961,0x340A, 0x11D0, [0xA9, 0x6B, cast(ubyte)0x00, cast(ubyte)0xC0, cast(ubyte)0x4F, cast(ubyte)0xD7, cast(ubyte)0x05, cast(ubyte)0xA2]};
//IID IID_IWebBrowser2 = {0xD30C1661, 0xCDAF, 0x11d0,[0x8A, 0x3E, 0x00, 0xC0, 0x4F, 0xC9, 0xE2, 0x6E]} ;


/**
 * Instances of this class implement the browser user interface
 * metaphor.  It allows the user to visualize and navigate through
 * HTML documents.
 * <p>
 * Note that although this class is a subclass of <code>Composite</code>,
 * it does not make sense to set a layout on it.
 * </p><p>
 * IMPORTANT: This class is <em>not</em> intended to be subclassed.
 * </p>
 * 
 * @since 3.0
 */
public class Browser : Composite , OleListener {

	OleFrame frame;
	OleControlSite site;
	OleAutomation automation;

	boolean back_var;
	boolean forward_var;
	Point location;
	Point size;
	IDispatch globalDispatch;
	char[] html;

	/* External Listener management */
	CloseWindowListener[] closeWindowListeners;
	LocationListener[] locationListeners;
	OpenWindowListener[] openWindowListeners;
	ProgressListener[] progressListeners;
	StatusTextListener[] statusTextListeners;
	TitleListener[] titleListeners;
	VisibilityWindowListener[] visibilityWindowListeners;
	
	enum{
	BeforeNavigate2 = 0xfa,
	CommandStateChange = 0x69,
	DocumentComplete = 0x103,
	NavigateComplete2 = 0xfc,
	NewWindow2 = 0xfb,
	OnVisible = 0xfe,
	ProgressChange = 0x6c,
	RegisterAsBrowser = 0x228,
	StatusTextChange = 0x66,
	TitleChange = 0x71,
	WindowClosing = 0x107,
	WindowSetHeight = 0x10b,
	WindowSetLeft = 0x108,
	WindowSetTop = 0x109,
	WindowSetWidth = 0x10a,
	}
	
	static const short CSC_NAVIGATEFORWARD = 1;
	static const short CSC_NAVIGATEBACK = 2;
	
	enum{
		INET_E_DEFAULT_ACTION = 0x800C0011,
		URLPOLICY_ALLOW = 0x00,
		URLPOLICY_DISALLOW = 0x03,
		URLZONE_LOCAL_MACHINE = 0,
		URLZONE_INTRANET = 1,
		URLACTION_ACTIVEX_MIN = 0x00001200,
		URLACTION_ACTIVEX_MAX = 0x000013ff,
		URLACTION_JAVA_MIN = 0x00001C00,
		URLPOLICY_JAVA_LOW = 0x00030000,
		URLACTION_JAVA_MAX = 0x00001Cff,
	}
	
	enum{
		DISPID_AMBIENT_DLCONTROL = -5512,
		DLCTL_DLIMAGES = 0x00000010,
		DLCTL_VIDEOS = 0x00000020,
		DLCTL_BGSOUNDS = 0x00000040,
		DLCTL_NO_SCRIPTS = 0x00000080,
		DLCTL_NO_JAVA = 0x00000100,
		DLCTL_NO_RUNACTIVEXCTLS = 0x00000200,
		DLCTL_NO_DLACTIVEXCTLS = 0x00000400,
		DLCTL_DOWNLOADONLY = 0x00000800,
		DLCTL_NO_FRAMEDOWNLOAD = 0x00001000,
		DLCTL_RESYNCHRONIZE = 0x00002000,
		DLCTL_PRAGMA_NO_CACHE = 0x00004000,
		DLCTL_FORCEOFFLINE = 0x10000000,
		DLCTL_NO_CLIENTPULL = 0x20000000,
		DLCTL_SILENT = 0x40000000,
	}
	
	static const char[] ABOUT_BLANK = "about:blank"; //$NON-NLS-1$
	static const char[] URL_CAB = ".cab"; //$NON-NLS-1$
	static const char[] URL_DIRECTOR = "http://download.macromedia.com/pub/shockwave/cabs/director/sw.cab"; //$NON-NLS-1$
	static const char[] URL_JAVA = "http://java.sun.com/products/plugin/autodl/jinstall"; //$NON-NLS-1$
	static const char[] URL_JAVA_15 = "http://java.sun.com/update"; //$NON-NLS-1$

	/* Package Name */
	static const char[] PACKAGE_PREFIX = "org.eclipse.dwt.browser."; //$NON-NLS-1$

/**
 * Constructs a new instance of this class given its parent
 * and a style value describing its behavior and appearance.
 * <p>
 * The style value is either one of the style constants defined in
 * class <code>DWT</code> which is applicable to instances of this
 * class, or must be built by <em>bitwise OR</em>'ing together 
 * (that is, using the <code>int</code> "|" operator) two or more
 * of those <code>DWT</code> style constants. The class description
 * lists the style constants that are applicable to the class.
 * Style bits are also inherited from superclasses.
 * </p>
 *
 * @param parent a widget which will be the parent of the new instance (cannot be null)
 * @param style the style of widget to construct
 *
 * @exception IllegalArgumentException <ul>
 *    <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
 * </ul>
 * @exception SWTException <ul>
 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
 * </ul>
 * @exception SWTError <ul>
 *    <li>ERROR_NO_HANDLES if a handle could not be obtained for browser creation</li>
 * </ul>
 * 
 * @see #getStyle
 * 
 * @since 3.0
 */
public this(Composite parent, int style) {
	super(parent, style);
	frame = new OleFrame(this, DWT.NONE);
	
	
	try {
		site = new WebSite(frame, DWT.NONE, "Shell.Explorer"); //$NON-NLS-1$
	} catch (Exception e) {
		dispose();
		DWT.error (__FILE__, __LINE__, e.toString());
	}
	
	site.doVerb(OLE.OLEIVERB_INPLACEACTIVATE);
	automation = new OleAutomation(site);

	// When this Composite is dispose, dispose the automation as well
	(cast(Widget)this).handleEvent(this, DWT.Dispose, delegate(Event e){
		Browser br = cast(Browser)e.cData;
		assert(br);
		if(br.automation){
			br.automation.dispose();
			br.automation = null;
		}
	});

	(cast(Widget)this).handleEvent(this, DWT.Resize, delegate(Event e){
		Browser br = cast(Browser)e.cData;
		assert(br);
		br.frame.setBounds(br.getClientArea());
	});
	
	// TODO: <Shawn> can't work currently
//	site.addEventListener(BeforeNavigate2, this);
//	site.addEventListener(CommandStateChange, this);
//	site.addEventListener(DocumentComplete, this);
//	site.addEventListener(NavigateComplete2, this);
//	site.addEventListener(NewWindow2, this);
//	site.addEventListener(OnVisible, this);

//	site.addEventListener(ProgressChange, this);
//	site.addEventListener(StatusTextChange, this);
//	site.addEventListener(TitleChange, this);
//	site.addEventListener(WindowClosing, this);

//	site.addEventListener(WindowSetHeight, this);
//	site.addEventListener(WindowSetLeft, this);
//	site.addEventListener(WindowSetTop, this);
//	site.addEventListener(WindowSetWidth, this);
	
	
	automation.setProperty(RegisterAsBrowser, new Variant(true));
	
}

/**	 
 * Adds the listener to receive events.
 * <p>
 *
 * @param listener the listener
 *
 * @exception IllegalArgumentException <ul>
 *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
 * </ul>
 * 
 * @exception SWTError <ul>
 *    <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
 *    <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
 * </ul>
 *
 * @since 3.0
 */
public void addCloseWindowListener(CloseWindowListener listener) {
	assert(listener);
	checkWidget();
	if (listener is null) DWT.error (__FILE__, __LINE__, DWT.ERROR_NULL_ARGUMENT);	
	closeWindowListeners ~= listener;
}

/**	 
 * Adds the listener to receive events.
 * <p>
 *
 * @param listener the listener
 *
 * @exception IllegalArgumentException <ul>
 *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
 * </ul>
 * 
 * @exception SWTError <ul>
 *    <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
 *    <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
 * </ul>
 *
 * @since 3.0
 */
public void addLocationListener(LocationListener listener) {
	assert(listener);
	checkWidget();
	if (listener is null) DWT.error (__FILE__, __LINE__, DWT.ERROR_NULL_ARGUMENT);	
	locationListeners ~= listener;
}

/**	 
 * Adds the listener to receive events.
 * <p>
 *
 * @param listener the listener
 *
 * @exception IllegalArgumentException <ul>
 *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
 * </ul>
 * 
 * @exception SWTError <ul>
 *    <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
 *    <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
 * </ul>
 *
 * @since 3.0
 */
public void addOpenWindowListener(OpenWindowListener listener) {
	assert(listener);
	checkWidget();
	if (listener is null) DWT.error (__FILE__, __LINE__, DWT.ERROR_NULL_ARGUMENT);
	openWindowListeners ~= listener;
}

/**	 
 * Adds the listener to receive events.
 * <p>
 *
 * @param listener the listener
 *
 * @exception IllegalArgumentException <ul>
 *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
 * </ul>
 * 
 * @exception SWTError <ul>
 *    <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
 *    <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
 * </ul>
 *
 * @since 3.0
 */
public void addProgressListener(ProgressListener listener) {
	assert(listener);
	checkWidget();
	if (listener is null) DWT.error (__FILE__, __LINE__, DWT.ERROR_NULL_ARGUMENT);
	progressListeners ~= listener;
}

/**	 
 * Adds the listener to receive events.
 * <p>
 *
 * @param listener the listener
 *
 * @exception IllegalArgumentException <ul>
 *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
 * </ul>
 * 
 * @exception SWTError <ul>
 *    <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
 *    <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
 * </ul>
 *
 * @since 3.0
 */
public void addStatusTextListener(StatusTextListener listener) {
	assert(listener);
	checkWidget();
	if (listener is null) DWT.error (__FILE__, __LINE__, DWT.ERROR_NULL_ARGUMENT);
	statusTextListeners ~= listener;
}

/**	 
 * Adds the listener to receive events.
 * <p>
 *
 * @param listener the listener
 *
 * @exception IllegalArgumentException <ul>
 *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
 * </ul>
 * 
 * @exception SWTError <ul>
 *    <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
 *    <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
 * </ul>
 *
 * @since 3.0
 */
public void addTitleListener(TitleListener listener) {
	assert(listener);
	checkWidget();
	if (listener is null) DWT.error (__FILE__, __LINE__, DWT.ERROR_NULL_ARGUMENT);
	titleListeners ~= listener;
}

/**	 
 * Adds the listener to receive events.
 * <p>
 *
 * @param listener the listener
 *
 * @exception IllegalArgumentException <ul>
 *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
 * </ul>
 * 
 * @exception SWTError <ul>
 *    <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
 *    <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
 * </ul>
 *
 * @since 3.0
 */
public void addVisibilityWindowListener(VisibilityWindowListener listener) {
	assert(listener);
	checkWidget();
	if (listener is null) DWT.error (__FILE__, __LINE__, DWT.ERROR_NULL_ARGUMENT);
	visibilityWindowListeners ~= listener;
}

/**
 * Navigate to the previous session history item.
 *
 * @return <code>true</code> if the operation was successful and <code>false</code> otherwise
 *
 * @exception SWTError <ul>
 *    <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
 *    <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
 * </ul>
 *
 * @see #forward
 * 
 * @since 3.0
 */
public boolean back() {
	checkWidget();
	if (!back_var) return false;
	char[][] StringArr;
	StringArr ~= ("GoBack");
	int[] rgdispid = automation.getIDsOfNames(StringArr); //$NON-NLS-1$
	Variant pVarResult = automation.invoke(rgdispid[0]);
	return pVarResult !is null && pVarResult.getType() == OLE.VT_EMPTY;
}

protected void checkSubclass() {
// TODO:
//	String name = getClass().getName();
//	int index = name.lastIndexOf('.');
//	if (!Convert.substring (name, 0, index + 1).equals(PACKAGE_PREFIX)) {
//		DWT.error (__FILE__, __LINE__, SWT_ERROR_INVALID_SUBCLASS);
//	}
}

/**
 * Navigate to the next session history item.
 *
 * @return <code>true</code> if the operation was successful and <code>false</code> otherwise
 *
 * @exception SWTError <ul>
 *    <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
 *    <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
 * </ul>
 * 
 * @see #back
 * 
 * @since 3.0
 */
public boolean forward() {
	checkWidget();
	if (!forward_var) return false;
	char[][] StringArr;
	StringArr ~= ("GoForward");
	int[] rgdispid = automation.getIDsOfNames(StringArr); //$NON-NLS-1$
	Variant pVarResult = automation.invoke(rgdispid[0]);
	return pVarResult !is null && pVarResult.getType() == OLE.VT_EMPTY;
}

/**
 * Returns <code>true</code> if the receiver can navigate to the 
 * previous session history item, and <code>false</code> otherwise.
 *
 * @return the receiver's back command enabled state
 *
 * @exception SWTException <ul>
 *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
 * </ul>
 * 
 * @see #back
 */
public boolean isBackEnabled() {
	checkWidget();
	return back_var;
}

/**
 * Returns <code>true</code> if the receiver can navigate to the 
 * next session history item, and <code>false</code> otherwise.
 *
 * @return the receiver's forward command enabled state
 *
 * @exception SWTException <ul>
 *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
 * </ul>
 * 
 * @see #forward
 */
public boolean isForwardEnabled() {
	checkWidget();
	return forward_var;
}

/**
 * Returns the current URL.
 *
 * @return the current URL or an empty <code>String</code> if there is no current URL
 *
 * @exception SWTError <ul>
 *    <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
 *    <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
 * </ul>
 *
 * @see #setUrl
 * 
 * @since 3.0
 */
public char[] getUrl() {
	checkWidget();
	char[][] StringArr;
	StringArr ~= ("LocationURL");
	int[] rgdispid = automation.getIDsOfNames(StringArr); //$NON-NLS-1$
	Variant pVarResult = automation.getProperty(rgdispid[0]);
	if (pVarResult is null || pVarResult.getType() != OLE.VT_BSTR)
		return "";
	return pVarResult.getString();
}

/**
 * Refresh the current page.
 *
 * @exception SWTError <ul>
 *    <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
 *    <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
 * </ul>
 *
 * @since 3.0
 */
public void refresh() {
	checkWidget();
	char[][] StringArr;
	StringArr ~= ("Refresh");
	int[] rgdispid = automation.getIDsOfNames(StringArr); //$NON-NLS-1$
	automation.invoke(rgdispid[0]);
}

/**	 
 * Removes the listener.
 *
 * @param listener the listener
 *
 * @exception IllegalArgumentException <ul>
 *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
 * </ul>
 * 
 * @exception SWTError <ul>
 *    <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
 *    <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
 * </ul>
 * 
 * @since 3.0
 */
public void removeCloseWindowListener(CloseWindowListener listener) {
	checkWidget();
	if (listener is null) {
		DWT.error (__FILE__, __LINE__, DWT.ERROR_NULL_ARGUMENT);
	}
	
	TVector!(CloseWindowListener).remove(closeWindowListeners, listener);
}

/**	 
 * Removes the listener.
 *
 * @param listener the listener
 *
 * @exception IllegalArgumentException <ul>
 *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
 * </ul>
 * 
 * @exception SWTError <ul>
 *    <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
 *    <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
 * </ul>
 * 
 * @since 3.0
 */
public void removeLocationListener(LocationListener listener) {
	checkWidget();
	if (listener is null) DWT.error (__FILE__, __LINE__, DWT.ERROR_NULL_ARGUMENT);
	TVector!(LocationListener).remove(locationListeners, listener);
}

/**	 
 * Removes the listener.
 *
 * @param listener the listener
 *
 * @exception IllegalArgumentException <ul>
 *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
 * </ul>
 * 
 * @exception SWTError <ul>
 *    <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
 *    <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
 * </ul>
 * 
 * @since 3.0
 */
public void removeOpenWindowListener(OpenWindowListener listener) {
	checkWidget();
	if (listener is null) DWT.error (__FILE__, __LINE__, DWT.ERROR_NULL_ARGUMENT);
	TVector!(OpenWindowListener).remove(openWindowListeners, listener);
}

/**	 
 * Removes the listener.
 *
 * @param listener the listener
 *
 * @exception IllegalArgumentException <ul>
 *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
 * </ul>
 * 
 * @exception SWTError <ul>
 *    <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
 *    <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
 * </ul>
 * 
 * @since 3.0
 */
public void removeProgressListener(ProgressListener listener) {
	checkWidget();
	if (listener is null) DWT.error (__FILE__, __LINE__, DWT.ERROR_NULL_ARGUMENT);
	TVector!(ProgressListener).remove(progressListeners, listener);
}

/**	 
 * Removes the listener.
 *
 * @param listener the listener
 *
 * @exception IllegalArgumentException <ul>
 *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
 * </ul>
 * 
 * @exception SWTError <ul>
 *    <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
 *    <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
 * </ul>
 * 
 * @since 3.0
 */
public void removeStatusTextListener(StatusTextListener listener) {
	checkWidget();
	if (listener is null) DWT.error (__FILE__, __LINE__, DWT.ERROR_NULL_ARGUMENT);
	TVector!(StatusTextListener).remove(statusTextListeners, listener);
}

/**	 
 * Removes the listener.
 *
 * @param listener the listener
 *
 * @exception IllegalArgumentException <ul>
 *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
 * </ul>
 * 
 * @exception SWTError <ul>
 *    <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
 *    <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
 * </ul>
 * 
 * @since 3.0
 */
public void removeTitleListener(TitleListener listener) {
	checkWidget();
	if (listener is null) DWT.error (__FILE__, __LINE__, DWT.ERROR_NULL_ARGUMENT);
	TVector!(TitleListener).remove(titleListeners, listener);
}

/**	 
 * Removes the listener.
 *
 * @param listener the listener
 *
 * @exception IllegalArgumentException <ul>
 *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
 * </ul>
 * 
 * @exception SWTError <ul>
 *    <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
 *    <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
 * </ul>
 * 
 * @since 3.0
 */
public void removeVisibilityWindowListener(VisibilityWindowListener listener) {
	checkWidget();
	if (listener is null) DWT.error (__FILE__, __LINE__, DWT.ERROR_NULL_ARGUMENT);
	TVector!(VisibilityWindowListener).remove(visibilityWindowListeners, listener);
}

/**
 * Renders HTML.
 * 
 * @param html the HTML content to be rendered
 *
 * @return true if the operation was successful and false otherwise.
 *
 * @exception IllegalArgumentException <ul>
 *    <li>ERROR_NULL_ARGUMENT - if the html is null</li>
 * </ul>
 * 
 * @exception SWTError <ul>
 *    <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
 *    <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
 * </ul>
 *  
 * @see #setUrl
 * 
 * @since 3.0
 */
public boolean setText(char[] html) {
	checkWidget();
	if (html is null) DWT.error (__FILE__, __LINE__, DWT.ERROR_NULL_ARGUMENT);
	this.html = html;
	
	/*
	* Navigate to the blank page and insert the given html when
	* receiving the next DocumentComplete notification.  See the
	* MSDN article "Loading HTML content from a Stream".
	* 
	* Note.  Stop any pending request.  This is required to avoid displaying a
	* blank page as a result of consecutive calls to setUrl and/or setText.  
	* The previous request would otherwise render the new html content and
	* reset the html field before the browser actually navigates to the blank
	* page as requested below.
	*/
	char[][] StringArr;
	StringArr ~= ("Stop");
	int[] rgdispid = automation.getIDsOfNames(StringArr); //$NON-NLS-1$
	automation.invoke(rgdispid[0]);
	/* Note.  Internet Explorer can still fire DocumentComplete events from the previous
	 * requests that were stopped.  The DocumentComplete related to the blank page
	 * will follow.  The workaround is to verify the DocumentComplete event relates to
	 * the blank page.  
	 */
	char[][] StringArr2;
	StringArr2 ~= ("Navigate");
	StringArr2 ~= ("URL");
	rgdispid = automation.getIDsOfNames(StringArr2); //$NON-NLS-1$ //$NON-NLS-2$
	Variant[] rgvarg = new Variant[1];
	rgvarg[0] = new Variant(ABOUT_BLANK);
	int[] rgdispidNamedArgs = new int[1];
	rgdispidNamedArgs[0] = rgdispid[1];
	Variant pVarResult = automation.invoke(rgdispid[0], rgvarg, rgdispidNamedArgs);
	return pVarResult !is null && pVarResult.getType() == OLE.VT_EMPTY;
}

/**
 * Loads a URL.
 * 
 * @param url the URL to be loaded
 *
 * @return true if the operation was successful and false otherwise.
 *
 * @exception IllegalArgumentException <ul>
 *    <li>ERROR_NULL_ARGUMENT - if the url is null</li>
 * </ul>
 * 
 * @exception SWTError <ul>
 *    <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
 *    <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
 * </ul>
 *  
 * @see #getUrl
 * 
 * @since 3.0
 */
public boolean setUrl(char[] url) {
	checkWidget();
	if (url is null) {
		DWT.error (__FILE__, __LINE__, DWT.ERROR_NULL_ARGUMENT);
		return false;
	}
	html = null;
	char[][] StringArr;
	StringArr ~= "Navigate";
	StringArr ~= "URL";
	int[] rgdispid = automation.getIDsOfNames(StringArr); //$NON-NLS-1$ //$NON-NLS-2$
	Variant[] rgvarg = new Variant[1];
	rgvarg[0] = new Variant(url);
	int[] rgdispidNamedArgs = new int[1];
	rgdispidNamedArgs[0] = rgdispid[1];
	Variant pVarResult = automation.invoke(rgdispid[0], rgvarg, rgdispidNamedArgs);
	if (pVarResult !is null && pVarResult.getType() == OLE.VT_EMPTY)
		return true;
	return false;
	
}

/**
 * Stop any loading and rendering activity.
 *
 * @exception SWTError <ul>
 *    <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
 *    <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
 * </ul>
 *
 * @since 3.0
 */
public void stop() {
	checkWidget();
	char[][] StringArr;
	StringArr ~= ("Stop");
	int[] rgdispid = automation.getIDsOfNames(StringArr); //$NON-NLS-1$
	automation.invoke(rgdispid[0]);
}

/**
 * <Shawn> Though this function is declared as public, it's not for use outside
 * DWT. It is the implementation of interface OleListener method.
 */
public void handleEvent(OleEvent event) {
	/+
	switch (event.type) {
		case BeforeNavigate2 : {
			Variant varResult = event.arguments[1];
			char[] url = varResult.getString();
			LocationEvent newEvent = new LocationEvent(this);
			newEvent.display = getDisplay();
			newEvent.widget = this;
			newEvent.location = url;
			newEvent.doit = true;
			for (int i = 0; i < locationListeners.length; i++)
				locationListeners[i].changing(newEvent);
			Variant cancel = event.arguments[6];
			if (cancel !is null) {
				void* pCancel = cancel.getByRef();
				short val = newEvent.doit ? COM.VARIANT_FALSE : COM.VARIANT_TRUE;
				COM.MoveMemory(pCancel, &val, 2);
		   }					
			break;
		}
		case CommandStateChange : {
			boolean enabled = false;
			Variant varResult = event.arguments[0];
			int command = varResult.getInt();
			varResult = event.arguments[1];
			enabled = varResult.getBoolean();
			switch (command) {
				case CSC_NAVIGATEBACK : back_var = enabled; break;
				case CSC_NAVIGATEFORWARD : forward_var = enabled; break;
				default : break;
			}
			break;
		}
		case DocumentComplete: {
			Variant varResult = event.arguments[0];
			IDispatch dispatch = varResult.getDispatch();

			varResult = event.arguments[1];
			char[] url = varResult.getString();
			if (html !is null && url == (ABOUT_BLANK)) {
			int charCount = html.length;
				wchar[] chars = std.utf.toUTF16(html);
				html = null;
				int byteCount = OS.WideCharToMultiByte(OS.CP_UTF8, 0, chars, charCount, null, 0, null, null);
				/*
				* Note. Internet Explorer appears to treat the data loaded with 
				* nsIPersistStreamInit.Load as if it were encoded using the default
				* local charset.  There does not seem to be an API to set the
				* desired charset explicitely in this case.  The fix is to
				* prepend the UTF-8 Byte Order Mark signature to the data.
				*/
				byte[] UTF8BOM;
				UTF8BOM ~= cast(byte)0xEF;
				UTF8BOM ~= cast(byte)0xBB;
				UTF8BOM ~= cast(byte)0xBF;
				HGLOBAL hGlobal = OS.GlobalAlloc(OS.GMEM_FIXED, UTF8BOM.length + byteCount);
				if (hGlobal !is null) {
					OS.MoveMemory(hGlobal, UTF8BOM, UTF8BOM.length);
					OS.WideCharToMultiByte(OS.CP_UTF8, 0, chars, charCount, cast(char*)(hGlobal + UTF8BOM.length), byteCount, null, null);							
					IStream ppstm;
					/* 
					* Note.  CreateStreamOnHGlobal is called with the flag fDeleteOnRelease.
					* If the call succeeds the buffer hGlobal is freed automatically
					* when the _IStream object is released. If the call fails, free the buffer
					* hGlobal.
					*/
					if (COM.CreateStreamOnHGlobal(hGlobal, TRUE, &ppstm) == OS.S_OK) {
						char[][] StringArr;
						StringArr ~= "Document";
						int[] rgdispid = automation.getIDsOfNames(StringArr); //$NON-NLS-1$
						Variant pVarResult = automation.getProperty(rgdispid[0]);
						IDispatch dispatchDocument = pVarResult.getDispatch();
						IPersistStreamInit persistStreamInit;
						int result = dispatchDocument.QueryInterface(&COM.IIDIPersistStreamInit, cast(void**)&persistStreamInit);
						if (result == OS.S_OK) {
							if (persistStreamInit.InitNew() == OS.S_OK) {
								persistStreamInit.Load(ppstm);
							}
							persistStreamInit.Release();
						}
						pVarResult.dispose();
						/*
						* This code is intentionally commented.  The _IDispatch obtained from a Variant
						* did not increase the reference count for the enclosed interface.
						*/
						//dispatchDocument.Release();
						ppstm.Release();
					} else {
						OS.GlobalFree(hGlobal);
					}
				}
			} else {
				Variant variant = new Variant(automation);
				IDispatch top = variant.getDispatch();
				LocationEvent locationEvent = new LocationEvent(this);
				locationEvent.display = getDisplay();
				locationEvent.widget = this;
				locationEvent.location = url;
				locationEvent.top = (top is dispatch);
				for (int i = 0; i < locationListeners.length; i++)
					locationListeners[i].changed(locationEvent);
				/*
				 * This code is intentionally commented.  A Variant constructed from an
				 * OleAutomation object does not increase its reference count.  The _IDispatch
				 * obtained from this Variant did not increase the reference count for the
				 * OleAutomation instance either. 
				 */
				//top.Release();
				//variant.dispose();
				/*
				 * Note.  The completion of the page loading is detected as
				 * described in the MSDN article "Determine when a page is
				 * done loading in WebBrowser Control". 
				 */
				if (globalDispatch !is null && dispatch is globalDispatch) {
					/* document complete */
					globalDispatch = null;
					ProgressEvent progressEvent = new ProgressEvent(this);
					progressEvent.display = getDisplay();
					progressEvent.widget = this;
					for (int i = 0; i < progressListeners.length; i++)
						progressListeners[i].completed(progressEvent);
				}
			}
									
			/*
			* This code is intentionally commented.  This IDispatch was received
			* as an argument from the OleEvent and it will be disposed along with
			* the other arguments.  
			*/
			//dispatch.Release();
			break;
		}
		case NavigateComplete2: {
			Variant varResult = event.arguments[0];
			IDispatch dispatch = varResult.getDispatch();
			if (globalDispatch is null) globalDispatch = dispatch;
			break;
		}
		case NewWindow2 : {
			WindowEvent newEvent = new WindowEvent(this);
			newEvent.display = getDisplay();
			newEvent.widget = this;
			for (int i = 0; i < openWindowListeners.length; i++)
				openWindowListeners[i].open(newEvent);
			Browser browser = newEvent.browser;
			boolean doit = browser !is null && !browser.isDisposed();
			if (doit) {
				Variant variant = new Variant(browser.automation);
				IDispatch iDispatch = variant.getDispatch();
				Variant ppDisp = event.arguments[0];
				void* byref = ppDisp.getByRef();
				if (byref !is null) COM.MoveMemory(byref, &iDispatch, 4);
				/*
				* This code is intentionally commented.  A Variant constructed from an
				* OleAutomation object does not increase its reference count.  The _IDispatch
				* obtained from this Variant did not increase the reference count for the
				* OleAutomation instance either. 
				*/
				//variant.dispose();
				//iDispatch.Release();
			}
			Variant cancel = event.arguments[1];
			void* pCancel = cancel.getByRef();
			short val = doit ? COM.VARIANT_FALSE : COM.VARIANT_TRUE;
			COM.MoveMemory(pCancel, &val, 2);
			break;
		}
		case OnVisible : {
			Variant arg1 = event.arguments[0];
			boolean visible = arg1.getBoolean();
			WindowEvent newEvent = new WindowEvent(this);
			newEvent.display = getDisplay();
			newEvent.widget = this;
			if (visible) {
				for (int i = 0; i < visibilityWindowListeners.length; i++) {
					newEvent.location = location;
					newEvent.size = size;
					visibilityWindowListeners[i].show(newEvent);
					location = null;
					size = null;
				}
			} else {
				for (int i = 0; i < visibilityWindowListeners.length; i++)
					visibilityWindowListeners[i].hide(newEvent);
			}
			break;
		}
		case ProgressChange : {
			Variant arg1 = event.arguments[0];
			int nProgress = arg1.getType() != OLE.VT_I4 ? 0 : arg1.getInt(); // may be -1
			Variant arg2 = event.arguments[1];
			int nProgressMax = arg2.getType() != OLE.VT_I4 ? 0 : arg2.getInt();
			ProgressEvent newEvent = new ProgressEvent(this);
			newEvent.display = getDisplay();
			newEvent.widget = this;
			newEvent.current = nProgress;
			newEvent.total = nProgressMax;
			if (nProgress != -1) {
				for (int i = 0; i < progressListeners.length; i++)
					progressListeners[i].changed(newEvent);
			}
			break;
		}
		case StatusTextChange : {
			Variant arg1 = event.arguments[0];
			if (arg1.getType() == OLE.VT_BSTR) {
				char[] text = arg1.getString();
				StatusTextEvent newEvent = new StatusTextEvent(this);
				newEvent.display = getDisplay();
				newEvent.widget = this;
				newEvent.text = text;
				for (int i = 0; i < statusTextListeners.length; i++)
					statusTextListeners[i].changed(newEvent);
			}
			break;
		}
		case TitleChange : {
			Variant arg1 = event.arguments[0];
			if (arg1.getType() == OLE.VT_BSTR) {
				char[] title = arg1.getString();
				TitleEvent newEvent = new TitleEvent(this);
				newEvent.display = getDisplay();
				newEvent.widget = this;
				newEvent.title = title;
				for (int i = 0; i < titleListeners.length; i++)
					titleListeners[i].changed(newEvent);
			}
			break;
		}
		case WindowClosing : {
			WindowEvent newEvent = new WindowEvent(this);
			newEvent.display = getDisplay();
			newEvent.widget = this;
			for (int i = 0; i < closeWindowListeners.length; i++)
				closeWindowListeners[i].close(newEvent);
			Variant cancel = event.arguments[1];
			void* pCancel = cancel.getByRef();
			short val = COM.VARIANT_FALSE;
			COM.MoveMemory(pCancel, &val, 2);
			dispose();
			break;
		}
		case WindowSetHeight : {
			if (size is null) size = new Point(0, 0);
			Variant arg1 = event.arguments[0];
			size.y = arg1.getInt();
			break;
		}
		case WindowSetLeft : {
			if (location is null) location = new Point(0, 0);
			Variant arg1 = event.arguments[0];
			location.x = arg1.getInt();
			break;
		}
		case WindowSetTop : {
			if (location is null) location = new Point(0, 0);
			Variant arg1 = event.arguments[0];
			location.y = arg1.getInt();
			break;
		}
		case WindowSetWidth : {
			if (size is null) size = new Point(0, 0);
			Variant arg1 = event.arguments[0];
			size.x = arg1.getInt();
			break;
		}
		default : break;
	}
+/
	/*
	* Dispose all arguments passed in the OleEvent.  This must be
	* done to properly release any IDispatch reference that was
	* automatically addRef'ed when constructing the OleEvent.  
	*/
//	Variant[] arguments = event.arguments;
//	for (int i = 0; i < arguments.length; i++) 
//		arguments[i].dispose();
	
	}

} // end of class Browser

